﻿using System;
using System.Reflection;
using System.Resources;
using System.Text;
using WorldBankBBS.Common;

namespace WorldBankBBS.Translators
{
    public class AnsiTranslator : ITranslator
    {
        public ISession CurrentSession { get; set; }

        public int ResourceToBytes(byte[] buffer, string resourceName)
        {
            return ResourceToBytes(buffer, 0, buffer.Length, resourceName);
        }

        public int ResourceToBytes(byte[] buffer, int offset, int length, string resourceName)
        {
            ResourceManager rm = new ResourceManager("WorldBankBBS.Resources.StringTable", Assembly.GetAssembly(typeof(WorldBankBBS.Resources.StringTable)));
            return StringToBytes(buffer, offset, length, rm.GetString(resourceName));
        }

        public int StringToBytes(byte[] buffer, string text)
        {
            return StringToBytes(buffer, 0, buffer.Length, text);
        }

        public int StringToBytes(byte[] buffer, string text, params object[] values)
        {
            return StringToBytes(buffer, 0, buffer.Length, text, values);
        }

        public int StringToBytes(byte[] buffer, int offset, int length, string text)
        {
            return StringToBytes(buffer, offset, length, text, new object[0]);
        }

        public int StringToBytes(byte[] buffer, int offset, int length, string text, params object[] values)
        {
            if (text == null) throw new ArgumentNullException("text");
            if (buffer == null) throw new ArgumentNullException("buffer");
            if (buffer.Length < offset + length) throw new ArgumentException("Offset and length exceed buffer size.");

            var result = 0;

            var inTag = false;
            var token = new StringBuilder();

            foreach (char c in text)
            {
                if (c == '{' && !inTag)
                {
                    inTag = true;
                    token.Clear();
                    token.Append(c);
                }
                else if (c == '}' && inTag)
                {
                    inTag = false;
                    token.Append(c);

                    if (token.ToString().StartsWith("{$"))
                    {
                        var value = CurrentSession.GetValue(token.ToString());

                        foreach (var v in value)
                        {
                            if (v == 0) break;
                            buffer[offset + result++] = v;
                            if (result == length) return result;
                        }
                    }
                    else if (token.ToString().StartsWith("{{"))
                    {
                        var index = Convert.ToInt32(token.ToString().Substring(2, token.Length - 3));

                        if (index >= 0 && index < values.Length)
                        {
                            var sub = new byte[1024];
                            var l = StringToBytes(sub, values[index].ToString());

                            for (int ll = 0; ll < l; ++ll)
                            {
                                buffer[offset + result++] = sub[ll];
                                if (result == length) return result;
                            }
                        }
                    }
                    else if (token.ToString().StartsWith("{%"))
                    {
                        var sub = new byte[1024];
                        var l = ResourceToBytes(sub, token.ToString().Substring(2, token.Length - 3));
                        for (int ll = 0; ll < l; ++ll)
                        {
                            buffer[offset + result++] = sub[ll];
                            if (result == length) return result;
                        }
                    }
                    else
                    {
                        var sub = new byte[1024];
                        var l = Twist(sub, token.ToString());
                        for (int ll = 0; ll < l; ++ll)
                        {
                            buffer[offset + result++] = sub[ll];
                            if (result == length) return result;
                        }
                    }
                }
                else if (inTag)
                {
                    token.Append(c);
                }
                else
                {
                    var o = ASCIIEncoding.ASCII.GetBytes(new string(c, 1));

                    if (o.Length > 0)
                    {
                        buffer[offset + result++] = o[0];
                        if (result == length) return result;
                    }
                }
            }

            return result;
        }

        private int Twist(byte[] buffer, string token)
        {
            var result = 0;
            switch (token.ToLower())
            {
                case "{ulcorner}":
                    result = 1;
                    buffer[0] = 0xda;
                    break;

                case "{urcorner}":
                    result = 1;
                    buffer[0] = 0xbf;
                    break;

                case "{llcorner}":
                    result = 1;
                    buffer[0] = 0xc0;
                    break;

                case "{lrcorner}":
                    result = 1;
                    buffer[0] = 0xd9;
                    break;

                case "{utee}":
                    result = 1;
                    buffer[0] = 0xc2;
                    break;

                case "{btee}":
                    result = 1;
                    buffer[0] = 0xc1;
                    break;

                case "{ltee}":
                    result = 1;
                    buffer[0] = 0xc3;
                    break;

                case "{rtee}":
                    result = 1;
                    buffer[0] = 0xb4;
                    break;

                case "{hline}":
                    result = 1;
                    buffer[0] = 0xc4;
                    break;

                case "{vline}":
                    result = 1;
                    buffer[0] = 0xb3;
                    break;

                case "{cross}":
                    result = 1;
                    buffer[0] = 0xc5;
                    break;

                case "{crlf}":
                    result = 2;
                    buffer[0] = (byte)'\n';
                    buffer[1] = (byte)'\r';
                    break;

                case "{clear}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'2';
                    buffer[3] = (byte)'J';
                    buffer[4] = 0x1b;
                    buffer[5] = (byte)'[';
                    buffer[6] = (byte)'f'; 
                    break;

                case "{black}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'0';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{red}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'1';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{green}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'2';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{yellow}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'3';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{blue}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'4';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{purple}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'5';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{cyan}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'6';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{white}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'7';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{mgray}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'0';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{pink}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'1';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{lgreen}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'2';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{orange}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'3';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{lblue}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'4';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{brown}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'5';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{dgray}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'6';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{lgray}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'3';
                    buffer[3] = (byte)'7';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{rvon}":
                    result = 7;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'4';
                    buffer[3] = (byte)'7';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'1';
                    buffer[6] = (byte)'m';
                    break;

                case "{rvoff}":
                    result = 8;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'4';
                    buffer[3] = (byte)'0';
                    buffer[4] = (byte)';';
                    buffer[5] = (byte)'2';
                    buffer[6] = (byte)'2';
                    buffer[7] = (byte)'m';
                    break;

                case "{up}":
                    result = 4;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'1';
                    buffer[3] = (byte)'A';
                    break;

                case "{down}":
                    result = 4;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'1';
                    buffer[3] = (byte)'B';
                    break;

                case "{left}":
                    result = 4;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'1';
                    buffer[3] = (byte)'D';
                    break;

                case "{right}":
                    result = 4;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'1';
                    buffer[3] = (byte)'C';
                    break;

                case "{home}":
                    result = 3;
                    buffer[0] = 0x1b;
                    buffer[1] = (byte)'[';
                    buffer[2] = (byte)'f';
                    break;
            }

            return result;
        }

        public string FromBytes(byte[] buffer)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");

            return ASCIIEncoding.ASCII.GetString(buffer);
        }

        public string FromBytes(byte[] buffer, int offset, int length)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");
            if (buffer.Length < offset + length) throw new ArgumentException("Offset and length exceed buffer size.");

            var b = new byte[length];

            for (int i = 0; i < length; ++i)
            {
                b[i] = buffer[i];
            }

            return ASCIIEncoding.ASCII.GetString(b);
        }
    }
}
